/*
 *      NetworkCommunicator.java
 *      
 *      Copyright 2010 Mark Kelly <markkelly0808@gmai.com>
 *      
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */
package org.RCSAgent.communication.networking;
 
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Arrays;

/**
 * NetworkCommunicator provides the network communication back end for RCSAgent
 */
public class NetworkCommunicator {
	public static final int DEFAULT_MESSAGE_SIZE = 4096;
	public static final NullPointerException NO_PACKET_EXCEPTION = new NullPointerException("There is no packet");
	public static final String DEFAULT_HOST_NAME = "localhost";
	public static final int DEFAULT_PORT = 6000;
	
	private DatagramSocket socket;
	private DatagramPacket receviedPacket;
	private int messageSize;
	private InetAddress host;
	private int port;
	
	/**
	 * NetworkCommunicator constructor
	 * 
	 * @param hostName the host name of the server
	 * @param hostPort the port to connect to on the host
	 * @param messageSize the size, in bytes, of messages sent between this object and the server
	 */
	public NetworkCommunicator(String hostName, int hostPort, int messageSize) {		
		try {
			host = InetAddress.getByName(hostName);
		}
		catch (UnknownHostException e) {
			// TODO recover from exception
			e.printStackTrace();
		}
		
		port = hostPort;
		this.messageSize = messageSize;
		
		try {
			socket = new DatagramSocket();
		}
		catch (SocketException e) {
			// TODO recover from exception
			e.printStackTrace();
		}
		
		receviedPacket = null;
	}
	
	/**
	 * NetworkCommunicator constructor
	 * 
	 * This constructor uses a default value for the message size.
	 * 
	 * @param hostName the host name of the server
	 * @param hostPort the port to connect to on the host
	 */
	public NetworkCommunicator(String hostName, int hostPort) {
		this(hostName, hostPort, DEFAULT_MESSAGE_SIZE);
	}
	
	/**
	 * NetworkCommunicator constructor
	 * 
	 * This constructor uses a default value for the hostname and message size
	 * 
	 * @param hostPort the port to connect to on the host
	 */
	public NetworkCommunicator(int hostPort) {
		this(DEFAULT_HOST_NAME, hostPort);
	}
	
	/**
	 * NetworkCommunicator constructor
	 * 
	 * This constructor uses a default value for the host port and message size
	 * 
	 * @param hostName the host name of the server
	 */
	public NetworkCommunicator(String hostName) {
		this(hostName, DEFAULT_PORT);
	}
	
	/**
	 * The default NetworkCommunicator constructor
	 */
	public NetworkCommunicator() {
		this(DEFAULT_PORT);
	}
	
	/**
	 * Ensures that this object's socket is closed before this object 
	 * is destroyed.
	 */
	public void finalize() {
		socket.close();
	}
	
	/**
	 * Recieves a packet.
	 */
	public void receive() {
		byte[] buffer = new byte[messageSize];
		receviedPacket = new DatagramPacket(buffer, messageSize);
		
		try {
			socket.receive(receviedPacket);
		}
		catch (SocketException e) {
			// TODO recover from exception
			e.printStackTrace();
		}
		catch (IOException e) {
			// TODO recover from exception
			e.printStackTrace();
		}
		
		/* 
		 * That this object will always be sending a packet to the port 
		 * from which it received its last packet
		 */
		port = receviedPacket.getPort();
	}
	
	/**
	 * @return the String representation of the bytes in the received packet
	 */
	public String getPacketString() {
		if (receviedPacket == null)
			throw NO_PACKET_EXCEPTION;
			
		return new String(receviedPacket.getData());
	}
	
	/**
	 * Send a message to the host
	 */
	public void send(String message) {
		byte[] buffer = Arrays.copyOf(message.getBytes(), messageSize);
		
		DatagramPacket packet = new DatagramPacket(buffer, messageSize, host, port);
		
		try {
			socket.send(packet);
		}
		catch (IOException e) {
			// TODO recover from exception
			e.printStackTrace();
		}
	}
}
